package app.tauri.libsql

import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke

@TauriPlugin
class LibsqlPlugin(private val activity: Activity) : Plugin(activity) {

    // Basic LibSQL operations
    @Command
    fun connect(invoke: Invoke) {
        val options = invoke.parseArgs(ConnectOptions::class.java)
        // TODO: Implement using LibSQL Android SDK
        // val connection = LibSQL.connect(options.localPath, options.url, options.authToken)
        invoke.resolve(JSObject().put("connectionId", "android-connection-id"))
    }

    @Command
    fun execute(invoke: Invoke) {
        val options = invoke.parseArgs(ExecuteOptions::class.java)
        // TODO: Implement SQL execution with LibSQL Android SDK
        // val result = connection.execute(options.sql, options.params)
        invoke.resolve(JSObject().put("rowsAffected", 1))
    }

    @Command
    fun query(invoke: Invoke) {
        val options = invoke.parseArgs(QueryOptions::class.java)
        // TODO: Implement SQL query with LibSQL Android SDK
        // val result = connection.query(options.sql, options.params)
        invoke.resolve(JSObject().apply {
            put("columns", arrayOf<String>())
            put("rows", arrayOf<Array<Any>>())
        })
    }

    @Command
    fun sync(invoke: Invoke) {
        val options = invoke.parseArgs(SyncOptions::class.java)
        // TODO: Implement sync with LibSQL Android SDK
        // connection.sync()
        invoke.resolve()
    }

    @Command
    fun close(invoke: Invoke) {
        val options = invoke.parseArgs(CloseOptions::class.java)
        // TODO: Implement connection close with LibSQL Android SDK
        // connection.close()
        invoke.resolve()
    }

    // Vector operations
    @Command
    fun vector_top_k(invoke: Invoke) {
        val options = invoke.parseArgs(VectorTopKOptions::class.java)
        // TODO: Implement vector_top_k using LibSQL Android SDK
        // Example implementation:
        // val sql = "SELECT * FROM vector_top_k('${options.indexName}', vector32('${options.queryVector}'), ${options.k})"
        // val result = connection.query(sql)
        invoke.resolve(JSObject().apply {
            put("columns", arrayOf("id"))
            put("rows", arrayOf<Array<Any>>())
        })
    }

    @Command
    fun vector_distance_cos(invoke: Invoke) {
        val request = invoke.parseArgs(VectorDistanceRequest::class.java)
        // TODO: Implement cosine distance calculation
        // Example implementation:
        // val vector1Sql = convertVectorToSql(request.vector1)
        // val vector2Sql = convertVectorToSql(request.vector2)
        // val sql = "SELECT vector_distance_cos($vector1Sql, $vector2Sql)"
        // val result = connection.query(sql)
        invoke.resolve(JSObject().put("distance", 0.5))
    }

    @Command
    fun vector_distance_l2(invoke: Invoke) {
        val request = invoke.parseArgs(VectorDistanceRequest::class.java)
        // TODO: Implement L2 distance calculation
        // Similar to cosine distance but using vector_distance_l2
        invoke.resolve(JSObject().put("distance", 1.0))
    }

    @Command
    fun vector_extract(invoke: Invoke) {
        val request = invoke.parseArgs(VectorExtractRequest::class.java)
        // TODO: Implement vector extraction from binary format
        // val sql = "SELECT vector_extract(?)"
        // val result = connection.query(sql, arrayOf(request.vectorBlob))
        invoke.resolve(JSObject().put("vector", arrayOf(0.1, 0.2, 0.3, 0.4)))
    }

    @Command
    fun create_vector_index(invoke: Invoke) {
        val request = invoke.parseArgs(CreateVectorIndexRequest::class.java)
        // TODO: Implement vector index creation
        // val settingsStr = request.settings?.joinToString("', '") ?: ""
        // val sql = "CREATE INDEX ${request.indexName} ON ${request.tableName}(libsql_vector_idx(${request.columnName}${if (settingsStr.isNotEmpty()) ", '$settingsStr'" else ""}))"
        // val rowsAffected = connection.execute(sql)
        invoke.resolve(JSObject().apply {
            put("rowsAffected", 1)
            put("lastInsertRowid", null)
        })
    }

    // Data classes for request parsing
    data class ConnectOptions(
        val localPath: String,
        val url: String?,
        val authToken: String?
    )

    data class ExecuteOptions(
        val connectionId: String,
        val sql: String,
        val params: Array<Value>?,
        val namedParams: Map<String, Value>?
    )

    data class QueryOptions(
        val connectionId: String,
        val sql: String,
        val params: Array<Value>?,
        val namedParams: Map<String, Value>?
    )

    data class SyncOptions(
        val connectionId: String
    )

    data class CloseOptions(
        val connectionId: String
    )

    data class VectorTopKOptions(
        val connectionId: String,
        val indexName: String,
        val queryVector: Array<Float>,
        val k: Int,
        val vectorType: String?
    )

    data class VectorDistanceRequest(
        val connectionId: String,
        val vector1: Value,
        val vector2: Value
    )

    data class VectorExtractRequest(
        val connectionId: String,
        val vectorBlob: ByteArray
    )

    data class CreateVectorIndexRequest(
        val connectionId: String,
        val indexName: String,
        val tableName: String,
        val columnName: String,
        val settings: Array<String>?
    )

    data class Value(
        val type: String, // "Null", "Integer", "Real", "Text", "Blob", "Vector"
        val value: Any?
    )

    // Helper function to convert Value to LibSQL parameter format
    private fun convertVectorToSql(value: Value): String {
        return when (value.type) {
            "Vector" -> {
                val vectorData = value.value as Map<String, Any>
                val data = vectorData["data"] as Array<Float>
                val vectorType = vectorData["vectorType"] as String
                val functionName = when (vectorType) {
                    "Float64" -> "vector64"
                    "Float32" -> "vector32"
                    "Float16" -> "vector16"
                    "FloatB16" -> "vectorb16"
                    "Float8" -> "vector8"
                    "Float1Bit" -> "vector1bit"
                    else -> "vector32"
                }
                "$functionName('${data.joinToString(",")}')"
            }
            else -> "NULL"
        }
    }
} 